package com.life.controller;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;

import com.life.constants.Constants;
import com.life.model.LifeModel;
import com.life.model.event.ModelBoardCompleteEvent;
import com.life.model.event.ModelBoardCreateEvent;
import com.life.model.event.ModelBoardResetEvent;
import com.life.model.event.ModelBoardUpdateEvent;
import com.life.view.AbstractAdapter;
import com.life.view.SliderAdapter;

public class SimulationController extends AbstractController
{

    AbstractAdapter playAdapter = null;

    AbstractAdapter stopAdapter = null;

    AbstractAdapter pauseAdapter = null;

    AbstractAdapter spinnerAdapter = null;

    AbstractAdapter recordAdapter = null;

    SliderAdapter sliderAdapter = null;

    Timer scheduler = null;

    SimulationTask simulationTask = null;

    int simulationOffset = Constants.SIMULATION_OFFSET;

    int simulationMax = Constants.SPEED_MAXIMUM * simulationOffset
	    + simulationOffset;

    int currentSpeed = Constants.SPEED_START * simulationOffset;

    long oldSpeedUpdateTime = System.currentTimeMillis();

    boolean playing = false;

    public SimulationController(LifeModel model,
	    ArrayList<AbstractAdapter> adapterList)
    {
	super(model, adapterList);

	playAdapter = adapterList.get(0);
	stopAdapter = adapterList.get(1);
	pauseAdapter = adapterList.get(2);
	recordAdapter = adapterList.get(3);
	sliderAdapter = (SliderAdapter)adapterList.get(4);
	spinnerAdapter = adapterList.get(5);

	model.addModelEventsListener(this);
	
	stopSimulation();

    }

    public void doUpdate(AbstractAdapter adapter, Object data)
    {
	if (adapter == playAdapter)
	{
	    startSimulation();
	} else if (adapter == stopAdapter)
	{
	    stopSimulation();
	} else if (adapter == pauseAdapter)
	{
	    pauseSimulation();
	} else if (adapter == recordAdapter)
	{
	    recordSimulation();
	} else if (adapter == sliderAdapter)
	{

	} else if (adapter == spinnerAdapter)
	{
	    changeSimulationSpeed();
	}
    }

    public void recordSimulation()
    {
	playAdapter.setEnabled(false);
	pauseAdapter.setEnabled(true);
	stopAdapter.setEnabled(true);
	recordAdapter.setEnabled(true);
	sliderAdapter.setEnabled(false);
    }

    public void startSimulation()
    {
	if (scheduler != null)
	{
	    scheduler.cancel();
	}

	int oldSpeed = currentSpeed;
	long delay = oldSpeed
		- (System.currentTimeMillis() - oldSpeedUpdateTime);
	delay = Math.max(0, delay);

	currentSpeed = ((Integer) spinnerAdapter.get()).intValue();

	scheduler = new Timer();
	scheduler.schedule(createSimulationTask(), delay, simulationMax
		- (currentSpeed * simulationOffset));

	playAdapter.setEnabled(false);
	pauseAdapter.setEnabled(true);
	stopAdapter.setEnabled(true);
	recordAdapter.setEnabled(true);
	sliderAdapter.setEnabled(false);

	playing = true;
    }

    public void changeSimulationSpeed()
    {
	if (playing)
	{
	    pauseSimulation();
	    startSimulation();
	}
    }

    public void stopSimulation()
    {
	pauseSimulation();

	playAdapter.setEnabled(true);
	pauseAdapter.setEnabled(false);
	stopAdapter.setEnabled(false);
	recordAdapter.setEnabled(false);
	sliderAdapter.setEnabled(true);

	playing = false;
    }

    public void pauseSimulation()
    {
	if (scheduler != null)
	{
	    scheduler.cancel();
	}

	playAdapter.setEnabled(true);
	pauseAdapter.setEnabled(false);
	stopAdapter.setEnabled(true);
	recordAdapter.setEnabled(false);
	sliderAdapter.setEnabled(true);

	playing = false;
    }

    public void modelBoardUpdated(ModelBoardUpdateEvent evt)
    {
	theModel = evt.getModel();
    }
    
    public void modelBoardReset(ModelBoardResetEvent event)
    {
	sliderAdapter.reset();
    }

    protected SimulationTask createSimulationTask()
    {
	return new SimulationTask(theModel);
    }

    class SimulationTask extends TimerTask
    {
	LifeModel theModel = null;

	public SimulationTask(LifeModel model)
	{
	    theModel = model;
	}

	public void setModel(LifeModel model)
	{
	    theModel = model;
	}

	public void run()
	{
	    theModel.runRule();
	    sliderAdapter.increment();
	}
    }

}
